package jmine.tec.utils.poi;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

/**
 * Esta classe lê apenas a primeira Worksheet!
 * 
 * @author takeshi
 */
public class SpreadsheetReader implements HSSFListener, Iterable<SpreadsheetRow> {

    private Map<Integer, SpreadsheetRow> rows = new TreeMap<Integer, SpreadsheetRow>();

    private SSTRecord table;

    private int lastColumn = -1;

    private int lastRow = -1;

    /**
     * @param source the source
     * @throws IOException e
     */
    public SpreadsheetReader(final InputStream source) throws IOException {
        POIFSFileSystem poifs = new POIFSFileSystem(source);
        InputStream din = poifs.createDocumentInputStream("Workbook");
        try {
            HSSFRequest req = new HSSFRequest();
            req.addListener(this, NumberRecord.sid);
            req.addListener(this, SSTRecord.sid);
            req.addListener(this, LabelSSTRecord.sid);
            HSSFEventFactory factory = new HSSFEventFactory();
            factory.processEvents(req, din);
        } finally {
            din.close();
        }
    }

    /**
     * @param index Index
     * @return Row
     */
    private SpreadsheetRow getOrCreate(final int index) {
        SpreadsheetRow row = this.rows.get(index);
        if (row == null) {
            row = new SpreadsheetRow(index);
            this.rows.put(index, row);
        }
        return row;
    }

    /**
     * @return the number of rows;
     */
    public int getRowCount() {
        return this.rows.size();
    }

    /**
     * @return the iterator for the Rows
     */
    public Iterator<SpreadsheetRow> getRowsIterator() {
        return this.rows.values().iterator();
    }

    /**
     * @param record {@link Record}
     */
    public void processRecord(final Record record) {
        switch (record.getSid()) {
        case NumberRecord.sid:
            NumberRecord nr = (NumberRecord) record;
            if (this.isSameSheet(nr)) {
                this.getOrCreate(nr.getRow()).getList().put(Integer.valueOf(nr.getColumn()), Double.valueOf(nr.getValue()));
            } else {
                return;
            }
            break;

        case SSTRecord.sid:
            this.table = (SSTRecord) record;

            break;

        case LabelSSTRecord.sid:
            LabelSSTRecord lr = (LabelSSTRecord) record;
            if (this.isSameSheet(lr)) {
                this.getOrCreate(lr.getRow()).getList().put(Integer.valueOf(lr.getColumn()), this.table.getString(lr.getSSTIndex()));
            } else {
                return;
            }
            break;

        default:
            return;
        }
    }

    /**
     * @param index int index
     * @return {@link SpreadsheetRow}
     */
    public SpreadsheetRow getRow(final int index) {
        return this.rows.get(index);
    }

    /**
     * @param cell Cell
     * @return true if its still the same sheet
     */
    private boolean isSameSheet(final CellValueRecordInterface cell) {
        if (this.lastRow < cell.getRow()) {
            this.lastRow = cell.getRow();
            this.lastColumn = -1;
            return true;
        } else if (this.lastRow == cell.getRow()) {
            if (this.lastColumn < cell.getColumn()) {
                this.lastColumn = cell.getColumn();
                return true;
            }
        }
        return false;
    }

    /**
     * @return the row iterator
     */
    public Iterator<SpreadsheetRow> iterator() {
        return this.getRowsIterator();
    }
}
